﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OOLib
{
   /// <summary>
   /// Service which specifies a storable and printable document 
   /// </summary>
   /// <remarks>
   /// All major document-like components should support this service instead of simple components which supports 
   /// a ::com::sun::star::frame::Controller or pure ::com::sun::star::awt::XWindow only. 
   /// </remarks>
   public class OfficeDocument : Component, IModel, IStorable, IServiceInfo
   {
      #region Constructors

      /// <summary>
      /// Constructor
      /// </summary>
      /// <remarks>
      /// After using this constructor, the ComObj must be set manually!
      /// </remarks>
      public OfficeDocument() :
         base()
      {
         _ServiceInfoImpl = new ServiceInfoImpl(this);
      }

      /// <summary>
      /// Constructor
      /// </summary>
      /// <param name="comObject">COM object</param>
      public OfficeDocument(object comObject) :
         base(comObject)
      {
         _ServiceInfoImpl = new ServiceInfoImpl(this);
      }

      #endregion

      private static string[] DocTypes = new[] 
         { 
            "", 
            "private:factory/swriter",
            "private:factory/scalc",
            "private:factory/sdraw",
            "private:factory/smath"
         };

      /// <summary>
      /// Gets the document type of this document.
      /// </summary>
      public DocType DocType
      {
         get
         {
            CheckDisposed();

            if (SupportsService("com.sun.star.text.TextDocument"))
            {
               return OOLib.DocType.Writer;
            }
            else if (SupportsService("com.sun.star.text.SpreadsheetDocument"))
            {
               return OOLib.DocType.Calc;
            }
            else if (SupportsService("com.sun.star.text.DrawingDocument"))
            {
               return OOLib.DocType.Draw;
            }
            else if (SupportsService("com.sun.star.text.FormulaProperties"))
            {
               return OOLib.DocType.Math;
            }
            else
            {
               return OOLib.DocType.Unknown;
            }
         }
      }

      #region IModel

      /// <summary>
      /// Informs a model about its resource description.
      /// </summary>
      /// <param name="url">Specifies the ressource.</param>
      /// <param name="arguments">Are optional arguments for that ressource (see ::com::sun::star::document::MediaDescriptor)</param>
      /// <returns>true for success, false otherwise</returns>
      /// <remarks>!!! Not tested !!!</remarks>
      public bool AttachResource(string url, object[] arguments)
      {
         CheckDisposed();
         bool success = (bool)InvokeMethod("attachResource", url, arguments);
         OnUrlChanged();
         return success;
      }

      /// <summary>
      /// Gets information about the location of this model.
      /// </summary>
      /// <remarks>!!! Not tested !!!</remarks>
      public string Url
      {
         get
         {
            CheckDisposed();
            return (string)InvokeMethod("getURL");
         }
      }

      /// <summary>
      /// Call this to notify others that the Url might have changed.
      /// </summary>
      protected virtual void OnUrlChanged()
      {
         NotifyPropertyChanged("Url");
      }

      /// <summary>
      /// Gets read acces on currently representation of the ::com::sun::star::document::MediaDescriptor of this model which describes the model and his state
      /// </summary>
      /// <remarks>!!! Not tested !!!</remarks>
      public object[] Args 
      { 
         get
         {
            CheckDisposed();
            return (object[])InvokeMethod("getArgs");
         }
      }

      /// <summary>
      /// Is called whenever a new controller is created for this model. 
      /// </summary>
      /// <param name="controller">A new controller for this model</param>
      /// <remarks>
      /// The ::com::sun::star::lang::XComponent interface of the controller must be used to recognize when it is deleted. 
      /// !!! Not tested !!!
      /// </remarks>
      public void ConnectController(Controller controller)
      {
         CheckDisposed();
         InvokeMethod("connectController", controller.ComObj);
      }

      /// <summary>
      /// Is called whenever an existing controller should be deregistered at this model. 
      /// </summary>
      /// <param name="controller">The existing controller which should be deregistered </param>
      /// <remarks>
      /// The ::com::sun::star::lang::XComponent interface of the controller must be used to recognize when it is deleted. 
      /// !!! Not tested !!!
      /// </remarks>
      public void DisconnectController(Controller controller)
      {
         CheckDisposed();
         InvokeMethod("disconnectController", controller.ComObj);
      }

      /// <summary>
      /// Suspends some notifications to the controllers which are used for display updates. 
      /// </summary>
      /// <remarks>
      /// The calls to XModel::lockControllers() and XModel::unlockControllers() may be nested and even overlapping, 
      /// but they must be in pairs. While there is at least one lock remaining, some notifications for display updates are not broadcasted. 
      /// !!! Not tested !!!
      /// </remarks>
      public void LockControllers()
      {
         CheckDisposed();
         InvokeMethod("lockControllers");
      }

      /// <summary>
      /// Resumes the notifications which were suspended by XModel::lockControllers(). 
      /// </summary>
      /// <remarks>
      /// The calls to XModel::lockControllers() and XModel::unlockControllers() may be nested and even overlapping, 
      /// but they must be in pairs. While there is at least one lock remaining, some notifications for display updates are not broadcasted. 
      /// !!! Not tested !!!
      /// </remarks>
      public void UnlockControllers()
      {
         CheckDisposed();
         InvokeMethod("unlockControllers");
      }

      /// <summary>
      /// Gets if there is at least one lock remaining. 
      /// </summary>
      /// <remarks>
      /// While there is at least one lock remaining, some notifications for display updates are not broadcasted to the controllers. 
      /// !!! Not tested !!!
      /// </remarks>
      public bool HasControllerLocked 
      { 
         get
         {
            CheckDisposed();
            return (bool)InvokeMethod("hasControllerLocked");
         }
      }

      /// <summary>
      /// Gets access to the controller which currently controls this model 
      /// </summary>
      /// <remarks>
      /// If the controller which is active is a controller of this model, it will be returned. 
      /// If not, the controller which was the last active of this model is returned. 
      /// If no controller of this model ever was active, the controller first registered is returned. 
      /// If no controller is registered for this model, NULL is returned. 
      /// </remarks>
      public Controller CurrentController
      {
         get 
         {
            CheckDisposed();
            return GetObjectWrapper<Controller>(InvokeMethod("getCurrentController"));
         }
         set
         {
            CheckDisposed();
            InvokeMethod("setCurrentController", value == null ? null : value.ComObj);
         }
      }

      /// <summary>
      /// Provides read access on current selection on controller .
      /// </summary>
      /// <remarks>
      /// Returns the current selection in the current controller. If there is no current controller, it returns NULL
      /// </remarks>
      public ComObjectWrapper CurrentSelection 
      { 
         get 
         { 
            CheckDisposed();
            return GetObjectWrapper<ComObjectWrapper>(InvokeMethod("currentSelection"));
         }
      }

      #endregion

      #region IStorable

      /// <summary>
      /// Gets if object knows the location because it was loaded from there, or because it is stored there.
      /// </summary>
      /// <remarks>!!! Not tested !!!</remarks>
      public bool HasLocation
      {
         get 
         {
            CheckDisposed();
            return (bool)InvokeMethod("hasLocation"); 
         }
      }

      /// <summary>
      /// After XStorable::storeAsURL() it returns the URL the object was stored to.
      /// </summary>
      /// <returns>Returns the URL of the resource which is represented by this object.</returns>
      /// <remarks>!!! Not tested !!!</remarks>
      public string GetLocation()
      {
         CheckDisposed();
         return (string)InvokeMethod("getLocation");
      }

      /// <summary>
      /// Gets if the data store is readonly or opened readonly.
      /// </summary>
      /// <remarks>
      /// It is not possible to call XStorable::store() successfully when the data store is read-only. 
      /// !!! Not tested !!!
      /// </remarks>
      public bool IsReadonly
      {
         get 
         {
            CheckDisposed();
            return (bool)InvokeMethod("isReadonly"); 
         }
      }

      /// <summary>
      /// Stores the data to the URL from which it was loaded.
      /// </summary>
      /// <remarks>
      /// Only objects which know their locations can be stored.
      /// </remarks>
      public void Store()
      {
         CheckDisposed();
         InvokeMethod("store");
      }

      /// <summary>
      /// Stores the object's persistent data to a URL and makes this URL the new location of the object. 
      /// </summary>
      /// <param name="url">Specifies the new location of this component as url.</param>
      /// <param name="arguments">Optional parameters for saving (see ::com::sun::star::document::MediaDescriptor for further details)</param>
      /// <remarks>
      /// This is the normal behavior for UI's "save-as" feature.
      /// The change of the location makes it necessary to store the document in a format that the object can load. 
      /// For this reason the implementation of XStorable::storeAsURL() will throw an exception if a pure export filter is used, 
      /// it will accept only combined import/export filters. For such filters the method XStorable::storeToURL() must be 
      /// used that does not change the location of the object.
      /// </remarks>
      public void StoreAsUrl(string url, object[] arguments = null)
      {
         CheckDisposed();
         if(arguments == null)
         {
            arguments = new object[0];
         }
         InvokeMethod("storeAsUrl", url, arguments);
         OnUrlChanged();
      }

      /// <summary>
      /// Stores the object's persistent data to a file and makes this file the new location of the object. 
      /// </summary>
      /// <param name="fileName">Specifies the new location of this component as file name.</param>
      /// <param name="arguments">Optional parameters for saving (see ::com::sun::star::document::MediaDescriptor for further details)</param>
      /// <remarks>
      /// This is the normal behavior for UI's "save-as" feature.
      /// The change of the location makes it necessary to store the document in a format that the object can load. 
      /// For this reason the implementation of XStorable::storeAsURL() will throw an exception if a pure export filter is used, 
      /// it will accept only combined import/export filters. For such filters the method XStorable::storeToURL() must be 
      /// used that does not change the location of the object.
      /// </remarks>
      public void StoreAsFileName(string fileName, object[] arguments = null)
      {
         StoreAsUrl(FileNameToUrl(fileName), arguments);
      }

      /// <summary>
      /// Stores the object's persistent data to a URL and continues to be a representation of the old URL. 
      /// </summary>
      /// <param name="url">Specifies the new location of this component as url.</param>
      /// <param name="arguments">Optional parameters for saving (see ::com::sun::star::document::MediaDescriptor for further details)</param>
      /// <remarks>
      /// This is the normal behavior for UI's export feature.
      /// This method accepts all kinds of export filters, not only combined import/export filters because it 
      /// implements an exporting capability, not a persistence capability.
      /// !!! Not tested !!!
      /// </remarks>
      public void StoreToUrl(string url, object[] arguments = null)
      {
         CheckDisposed();
         if (arguments == null)
         {
            arguments = new object[0];
         }
         InvokeMethod("storeToUrl", url, arguments);
      }

      /// <summary>
      /// Stores the object's persistent data to a file and continues to be a representation of the old URL. 
      /// </summary>
      /// <param name="fileName">Specifies the new location of this component as file name.</param>
      /// <param name="arguments">Optional parameters for saving (see ::com::sun::star::document::MediaDescriptor for further details)</param>
      /// <remarks>
      /// This is the normal behavior for UI's export feature.
      /// This method accepts all kinds of export filters, not only combined import/export filters because it 
      /// implements an exporting capability, not a persistence capability.
      /// </remarks>
      public void StoreToFileName(string fileName, object[] arguments = null)
      {
         StoreToUrl(FileNameToUrl(fileName), arguments);
      }

      #endregion


      #region IServiceInfo
      //todo check who is really implementing the XServiceInfo interface

      private ServiceInfoImpl _ServiceInfoImpl;

      /// <summary>
      /// Gets the implementation name of the service implementation.
      /// </summary>
      public string ImplementationName
      {
         get
         {
            return _ServiceInfoImpl.ImplementationName;
         }
      }

      /// <summary>
      /// Tests whether the specified service is supported, i.e. implemented by the implementation.
      /// </summary>
      /// <param name="serviceName">Name of service to be tested </param>
      /// <returns>true, if service is supported, false otherwise</returns>
      public bool SupportsService(string serviceName)
      {
         return _ServiceInfoImpl.SupportsService(serviceName);
      }

      /// <summary>
      /// Provides the supported service names of the implementation, including also indirect service names.
      /// </summary>
      /// <returns>Returns a sequence of service names that are supported.</returns>
      public string[] GetSupportedServiceNames()
      {
         return _ServiceInfoImpl.GetSupportedServiceNames();
      }

      #endregion

      #region properties

      /// <summary>
      /// Gets or Sets the focus behaviour of the form controls in the document.
      /// </summary>
      /// <remarks>
      /// If set to true, upon loading the document, the form controls will be in design mode.
      /// If set to false, they will be alive, i.e. operational.
      /// With this, you may control if your document works primarily as a form document. 
      /// This property is optional!
      /// !!! Not tested !!!
      /// </remarks>
      public bool AutomaticControlFocus
      {
         get 
         {
            CheckDisposed(); 
            return (bool)GetProperty("AutomaticControlFocus");
         }
         set 
         {
            CheckDisposed(); 
            SetProperty("AutomaticControlFocus", value);
         }
      }

      /// <summary>
      /// Gets or sets the initial (on-load) behavior of the form controls in the document.
      /// </summary>
      /// <remarks>
      /// If set to true, upon loading the document, the form controls will be in design mode.
      /// If set to false, they will be alive, i.e. operational.
      /// With this, you may control if your document works primarily as a form document. 
      /// This property is optional!
      /// !!! Not tested !!!
      /// </remarks>
      public bool ApplyFormDesignMode
      {
         get 
         {
            CheckDisposed(); 
            return (bool)GetProperty("ApplyFormDesignMode");
         }
         set 
         {
            CheckDisposed(); 
            SetProperty("ApplyFormDesignMode", value);
         }
      }

      /// <summary>
      /// Gets a unique id for the document.
      /// </summary>
      /// <remarks>
      /// Once calculated, the id must not change until the document has been closed. Upon closing it will not be made persistent. 
      /// Thus, the document may get a different id every time it gets loaded.
      /// This property is optional!
      /// !!! Not tested !!!
      /// </remarks>
      public string RuntimeUID
      {
         get 
         {
            CheckDisposed(); 
            return (string)GetProperty("RuntimeUID");
         }
      }

      #endregion
   }
}
